package com.sam.core.dao.jdbc;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.BeanPropertySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.jdbc.core.simple.ParameterizedBeanPropertyRowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Repository;
import org.springframework.util.Assert;

import com.sam.core.domain.Page;

/**
 * jdbcTemplate 模板 主要包含了分页,bean参数查询和返回bean
 * @project EMan
 * @author Sam
 * @version
 * @date Apr 1, 2009 4:11:18 PM
 */
@Repository
@SuppressWarnings("unchecked")
public class BaseJdbcTemplate {

	protected Logger log = LoggerFactory.getLogger(getClass());
	@Autowired
	protected DataSource dataSource;
	@Autowired
	protected SimpleJdbcTemplate simpleJdbcTemplate;
	@Autowired
	protected JdbcTemplate jdbcTemplate;

	// 取得 from 位置的表达式
	private static final Pattern FROM_PATTERN = Pattern.compile("(^|\\s)(from)\\s",
			Pattern.CASE_INSENSITIVE);

	// 取得 order 位置的表达式
	private static final Pattern ORDER_PATTERN = Pattern.compile("\\s(order)(\\s)+by\\s",
			Pattern.CASE_INSENSITIVE);

	/**
	 * 由sql生成计算记录总数的sql
	 * @param sql
	 * @return
	 */
	public static String getCountSQL(String sql) {
		Matcher fMatcher = FROM_PATTERN.matcher(sql);
		if (!fMatcher.find()) {
			throw new IllegalArgumentException("no from clause found in query");
		}
		int fLoc = fMatcher.start(2);

		Matcher oMatcher = ORDER_PATTERN.matcher(sql);
		int oLoc = oMatcher.find() ? oMatcher.start(1) : sql.length();

		return "select count(*) " + sql.substring(fLoc, oLoc);
	}

	/**
	 * 分页方法
	 * @param sql 查询sql
	 * @param param 查询参数
	 * @param map 查询结果包装器
	 * @param page 分页参数
	 */
	public <T> void page(String sql, Class<T> clazz, Page page) {
		page.setTotalCount(queryForInt(getCountSQL(sql), null));
		page.setResult(simpleJdbcTemplate.query(getLimitSQLString(sql, page),
				resultBeanMapper(clazz)));

	}

	/**
	 * 组装分页sql
	 * @param sql
	 * @param page
	 * @return 返回组装后的分页sql
	 */
	private String getLimitSQLString(String sql, Page page) {
		StringBuffer sb = new StringBuffer();
		sb.append("SELECT * FROM (SELECT TOP ").append(page.getCount()).append(" * ");
		sb.append("FROM (SELECT TOP ").append(
				page.getPageSize() * (page.getFirst() / page.getPageSize() + 1)).append(" ");
		sb.append(sql.substring(6)).append(" ORDER BY ").append(page.getOrderBy()).append(" ")
				.append(page.getOrder()).append(" ) as aSysTable  ORDER BY ").append(
						page.getOrderBy()).append(" ").append(page.getInverseOrder()).append(
						" ) as bSysTable  ORDER BY ").append(page.getOrderBy()).append(" ").append(
						page.getOrder());
		return sb.toString();
	}

	/**
	 * 仅有一个命名参数的分页方法且默认命名参数名称为:query
	 * @param sql 查询sql,该sql中的命名参数必须为:query(参数用冒号加参数名，例如select * from tb where name=?)
	 * @param key 查询sql中的命名参数
	 * @param query sql参数值
	 * @param clazz 返回bean
	 * @param page 分页对象
	 */
	public void page(String sql, int id, Class clazz, Page page) {
		page.setTotalCount(simpleJdbcTemplate.queryForInt(getCountSQL(sql), id));
		page.setResult(simpleJdbcTemplate.query(getLimitSQLString(sql, page),
				resultBeanMapper(clazz), id));
	}

	/**
	 * 仅有一个命名参数的分页方法且默认命名参数名称为:query
	 * @param sql 查询sql,该sql中的命名参数必须为:query(参数用冒号加参数名，例如select * from tb where name=:query)
	 * @param key 查询sql中的命名参数
	 * @param query sql参数值
	 * @param clazz 返回bean
	 * @param page 分页对象
	 */
	public <T> void page(String sql, String query, Class<T> clazz, Page page) {
		page(sql, "query", query, clazz, page);
	}

	/**
	 * 仅有一个命名参数的分页方法
	 * @param sql 查询sql(参数用冒号加参数名，例如select * from tb where id=:id),则此时key为id
	 * @param key 查询sql中的命名参数
	 * @param query sql参数值
	 * @param clazz 返回bean
	 * @param page 分页对象
	 */
	public <T> void page(String sql, String key, Object query, Class<T> clazz, Page page) {
		Map<String, Object> map = new HashMap<String, Object>();
		map.put(key, query);
		page.setTotalCount(queryForInt(getCountSQL(sql), map));
		page.setResult(query(getLimitSQLString(sql, page), clazz, map));

	}

	/**
	 * 分页查询方法
	 * @param sql 查询sql(参数用冒号加参数名，例如select * from tb where id=:id)
	 * @param object查询参数bean
	 * @param clazz 返回的bean
	 * @param page 分页bean
	 */
	public <T> void page(String sql, Object object, Class<T> clazz, Page page) {
		if (object == null) {
			page(sql, clazz, page);
		} else if (object instanceof Map) {
			Map map = (Map) object;
			pageMap(sql, map, clazz, page);
		} else {
			page.setTotalCount(simpleJdbcTemplate.queryForInt(getCountSQL(sql),
					paramBeanMapper(object)));
			page.setResult(simpleJdbcTemplate.query(getLimitSQLString(sql, page),
					resultBeanMapper(clazz), paramBeanMapper(object)));
		}
	}

	/**
	 * 分页方法
	 * @param sql 查询sql(参数用冒号加参数名，例如select * from tb where id=:id)
	 * @param param 查询参数
	 * @param map 查询结果包装器
	 * @param page 分页参数
	 */
	public void pageMap(String sql, Map map, Class clazz, Page page) {
		page.setTotalCount(queryForInt(getCountSQL(sql), map));
		page.setResult(query(getLimitSQLString(sql, page), clazz, map));

	}

	/**
	 * 根据sql语句，返回对象集合
	 * @param sql语句(参数用冒号加参数名，例如select * from tb where id=:id)
	 * @param clazz类型
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象集合
	 */
	public <T> List<T> query(final String sql, Class clazz) {
		try {
			Assert.hasText(sql, "sql语句不正确!");

			return simpleJdbcTemplate.query(sql, resultBeanMapper(clazz));

		} catch (Exception e) {
			return new ArrayList<T>();
		}
	}

	/**
	 * 根据sql语句，返回对象集合
	 * @param sql语句(参数用冒号加参数名，例如select * from tb where id=:id)
	 * @param clazz类型
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象集合
	 */
	public <T> List<T> query(final String sql, Class clazz, Map parameters) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (parameters != null) {
				return simpleJdbcTemplate.query(sql, resultBeanMapper(clazz), parameters);
			} else {
				return simpleJdbcTemplate.query(sql, resultBeanMapper(clazz));
			}
		} catch (Exception e) {
			return new ArrayList<T>();
		}
	}

	/**
	 * 根据sql语句，返回对象集合
	 * @param sql语句(参数用冒号加参数名，例如select * from tb where id=:id)
	 * @param clazz类型
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象集合
	 */
	public List query(final String sql, Class clazz, Object... args) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (args == null) {
				return simpleJdbcTemplate.query(sql, resultBeanMapper(clazz));
			} else {
				return simpleJdbcTemplate.query(sql, resultBeanMapper(clazz), args);
			}
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 根据sql语句，返回对象
	 * @param sql语句(参数用冒号加参数名，例如select * from tb where id=:id)
	 * @param clazz类型
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象
	 */
	public Object queryForObject(final String sql, Class clazz, Map parameters) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (parameters != null) {
				return simpleJdbcTemplate.queryForObject(sql, resultBeanMapper(clazz), parameters);
			} else {
				return simpleJdbcTemplate.queryForLong(sql, resultBeanMapper(clazz));
			}
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 根据sql语句，返回对象
	 * @param sql语句(参数用冒号加参数名，例如select * from tb where id=:id)
	 * @param clazz类型
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象
	 */
	public Object queryForObject(final String sql, Class clazz, Object... objects) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (objects != null) {
				return simpleJdbcTemplate.queryForObject(sql, resultBeanMapper(clazz), objects);
			} else {
				return simpleJdbcTemplate.queryForLong(sql, resultBeanMapper(clazz));
			}
		} catch (Exception e) {
			return null;
		}
	}

	/**
	 * 根据sql语句，返回数值型返回结果
	 * @param sql语句(参数用冒号加参数名，例如select count(*) from tb where id=:id)
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象
	 */
	public long queryForLong(final String sql, Map parameters) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (parameters != null) {
				return simpleJdbcTemplate.queryForLong(sql, parameters);
			} else {
				return simpleJdbcTemplate.queryForLong(sql);
			}
		} catch (Exception e) {
			return 0;
		}
	}

	/**
	 * 根据sql语句，返回数值型返回结果
	 * @param sql语句(参数用冒号加参数名，例如select count(*) from tb where id=:id)
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象
	 */
	public int queryForInt(final String sql, Map parameters) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (parameters != null) {
				return simpleJdbcTemplate.queryForInt(sql, parameters);
			} else {
				return simpleJdbcTemplate.queryForInt(sql);
			}
		} catch (Exception e) {
			return 0;
		}
	}

	/**
	 * 根据sql语句，返回Map对象,对于某些项目来说，没有准备Bean对象，则可以使用Map代替Key为字段名,value为值
	 * @param sql语句(参数用冒号加参数名，例如select count(*) from tb where id=:id)
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象
	 */
	public Map<String, Object> queryForMap(final String sql, Map parameters) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (parameters != null) {
				return simpleJdbcTemplate.queryForMap(sql, parameters);
			} else {
				return simpleJdbcTemplate.queryForMap(sql);
			}
		} catch (Exception e) {
			return new HashMap<String, Object>();
		}
	}

	/**
	 * 根据sql语句，返回Map对象集合
	 * @see findForMap
	 * @param sql语句(参数用冒号加参数名，例如select count(*) from tb where id=:id)
	 * @param parameters参数集合(key为参数名，value为参数值)
	 * @return bean对象
	 */
	public List<Map<String, Object>> queryForListMap(final String sql, Map parameters) {
		try {
			Assert.hasText(sql, "sql语句不正确!");
			if (parameters != null) {
				return simpleJdbcTemplate.queryForList(sql, parameters);
			} else {
				return simpleJdbcTemplate.queryForList(sql);
			}
		} catch (Exception e) {
			return new ArrayList<Map<String, Object>>();
		}
	}

	/**
	 * 执行insert，update，delete等操作<br>
	 * 例如insert into users (name,login_name,password) values(:name,:loginName,:password)<br>
	 * 参数用冒号,参数为bean的属性名
	 * @param sql
	 * @param bean
	 */
	public int update(final String sql, Object bean) {
		Assert.hasText(sql, "sql语句不正确!");
		if (bean != null) {
			return simpleJdbcTemplate.update(sql, paramBeanMapper(bean));
		} else {
			return simpleJdbcTemplate.update(sql);
		}
	}

	/**
	 * 执行insert，update，delete等操作<br>
	 * 例如insert into users (name,login_name,password) values(:name,:loginName,:password)<br>
	 * 参数用冒号,参数为bean的属性名
	 * @param sql
	 * @param bean
	 */
	public int update(final String sql, Object... bean) {
		Assert.hasText(sql, "sql语句不正确!");
		if (bean != null) {
			return simpleJdbcTemplate.update(sql, bean);
		} else {
			return simpleJdbcTemplate.update(sql);
		}
	}

	/**
	 * 执行insert，update，delete等操作<br>
	 * 例如insert into users (name,login_name,password) values(:name,:login_name,:password)<br>
	 * 参数用冒号,参数为Map的key名
	 * @param sql
	 * @param parameters
	 */
	public int update(final String sql, Map parameters) {
		Assert.hasText(sql, "sql语句不正确!");
		if (parameters != null) {
			return simpleJdbcTemplate.update(sql, parameters);
		} else {
			return simpleJdbcTemplate.update(sql);
		}
	}

	public int jdbcUpdate(final String sql, String... parameters) {
		Assert.hasText(sql, "sql语句不正确!");
		return jdbcTemplate.update(sql, parameters);
	}

	/**
	 * 根据id数组删除 例如DELETE FROM Tips WHERE (id = ?)<br>
	 * @param sql
	 * @param ids
	 * @return
	 */
	public int[] batchDelete(String sql, String[] ids) {
		List<Object[]> batchArgs = new ArrayList<Object[]>();
		for (String string : ids) {
			batchArgs.add(new Object[] { string });
		}
		return simpleJdbcTemplate.batchUpdate(sql, batchArgs);
	}

	public int[] batchUpdate(String sql, List<Object> list) {
		Assert.hasText(sql, "sql语句不正确!");
		Assert.notEmpty(list, "sql语句不正确!");
		SqlParameterSource batchArgs[] = new SqlParameterSource[list.size()];
		for (int i = 0; i < list.size(); i++) {
			Object object = list.get(i);
			if (object instanceof Map) {
				batchArgs[i] = paramMapMapper((Map) object);
			} else {
				batchArgs[i] = (paramBeanMapper(object));
			}
		}
		return simpleJdbcTemplate.batchUpdate(sql, batchArgs);
	}

	public long executeForObjectReturnPk(final String sql, Object bean) {
		Assert.hasText(sql, "sql语句不正确!");
		if (bean != null) {
			return simpleJdbcTemplate.update(sql, paramBeanMapper(bean));
		} else {
			return simpleJdbcTemplate.update(sql);
		}
	}

	protected ParameterizedBeanPropertyRowMapper resultBeanMapper(Class clazz) {
		return ParameterizedBeanPropertyRowMapper.newInstance(clazz);
	}

	protected MapSqlParameterSource paramMapMapper(Map map) {
		return new MapSqlParameterSource(map);
	}

	protected BeanPropertySqlParameterSource paramBeanMapper(Object object) {
		return new BeanPropertySqlParameterSource(object);
	}

	public SimpleJdbcTemplate getSimpleJdbcTemplate() {
		return simpleJdbcTemplate;
	}
}
